package com.tian.service.user;

import com.alibaba.fastjson.JSON;
import com.tian.common.exception.BusinessException;
import com.tian.common.util.CommonResult;
import com.tian.common.util.DateUtils;
import com.tian.common.util.JwtGeneratorUtil;
import com.tian.common.util.RedisConstantPre;
import com.tian.dto.user.AuthLoginDto;
import com.tian.dto.user.ChargeUserLoginResDto;
import com.tian.entity.ChargeUser;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.redisson.api.RBitSet;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * {@code @description:} 登录抽象类
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024-04-07 16:42
 * {@code @version:} 1.0
 */
@Slf4j
@Component
public abstract class AbstractLoginService implements LoginService {

    public static ConcurrentHashMap<Integer, AbstractLoginService> LOGIN_TYPE_MAP = new ConcurrentHashMap<>();
    @Resource
    private RedissonClient redissonClient;

    @PostConstruct
    public void init() {
        LOGIN_TYPE_MAP.put(getLoginType(), this);
    }

    @Override
    public CommonResult<String> doLogin(AuthLoginDto authLoginDto) throws BusinessException {
        log.info("begin AbstractLogin.doLogin:" + authLoginDto);
        // 校验参数
        validate(authLoginDto);
        long exp=DateTime.now().plusMonths(1).toDate().getTime() / 1000;
        // 登录处理
        ChargeUser user = doProcessor(authLoginDto);
        RBitSet onlineUser =redissonClient.getBitSet(RedisConstantPre.USER_ONLINE_COUNT_PRE + DateUtils.formatCurrentDay());
        onlineUser.set(user.getId(), true);
        // 生成token
        String token = getToken(user, exp);
        // 缓存用户信息
        cacheUserInfo(user, exp, token);
        // 返回token
        return CommonResult.success(token);
    }

    private static String getToken(ChargeUser user, long exp) {
        Map<String, Object> payLoad = new HashMap<>();
        payLoad.put("userId", user.getId());
        payLoad.put("exp", exp);
        // 生成token---userId和时间戳为载荷，生成token
        log.info("生成token:" + payLoad);
        return JwtGeneratorUtil.generatorToken(payLoad);
    }

    private void cacheUserInfo(ChargeUser user, long exp, String token) {
        ChargeUserLoginResDto chargeUserLoginResDto = new ChargeUserLoginResDto();
        BeanUtils.copyProperties(user, chargeUserLoginResDto);
        // 缓存用户信息--手机号为key
        RBucket<String> bucket = redissonClient.getBucket(RedisConstantPre.USER_INFO_PHONE_PRE + user.getPhone());
        bucket.set(JSON.toJSONString(chargeUserLoginResDto));
        bucket.expire(exp, java.util.concurrent.TimeUnit.SECONDS);
        // 缓存用户信息--userId为key
        RBucket<String> bucketId = redissonClient.getBucket(RedisConstantPre.USER_INFO_ID_PRE + user.getId());
        bucketId.set(JSON.toJSONString(chargeUserLoginResDto));
        bucketId.expire(exp, java.util.concurrent.TimeUnit.SECONDS);
        // 缓存用户信息--token为key
        RBucket<String> bucketToken = redissonClient.getBucket(RedisConstantPre.TOKEN_PRE + token);
        bucketToken.set(JSON.toJSONString(chargeUserLoginResDto));
        bucketToken.expire(exp, java.util.concurrent.TimeUnit.SECONDS);
    }

    /**
     *  子类自行实现的抽象方法
     */

    public abstract int getLoginType();

    public abstract void validate(AuthLoginDto authLoginDto);

    public abstract ChargeUser doProcessor(AuthLoginDto authLoginDto);

}
